﻿#include "PluginManager.h"

#include "Plugin/IPlugin.h"

static PluginManager *g_pluginManager = nullptr;

const std::filesystem::path PluginManager::s_defaultPluginDir = "plugins";

PluginManager::PluginManager()
{
  assert(!g_pluginManager && "PluginManager already exists");
  g_pluginManager = this;
}

PluginManager::~PluginManager()
{
  CallHook("Destroy");

  if (g_pluginManager == this)
    g_pluginManager = nullptr;
}

PluginManager &PluginManager::GetInstance()
{
  return *g_pluginManager;
}

bool PluginManager::LoadPlugin(const std::string &name)
{
  std::filesystem::path pluginPath = GetPluginDir() / (name + GetPluginExt());

  if (!std::filesystem::exists(pluginPath))
  {
    pluginPath = GetPluginDir() / ("lib" + name + GetPluginExt());
    if (!std::filesystem::exists(pluginPath))
      return false;
  }
    

#ifdef _WIN32
  auto pluginDll = LoadLibraryA(pluginPath.string().c_str());
#else
  auto pluginDll = dlopen(pluginPath.c_str(), RTLD_LAZY);
#endif
  if (!pluginDll)
    return false;

  // 跨平台符号加载
#ifdef _WIN32
  auto plugin = reinterpret_cast<CreatePluginFunc>(GetProcAddress(pluginDll, "CreatePlugin"));
#else
  auto plugin = reinterpret_cast<CreatePluginFunc>(dlsym(pluginDll, "CreatePlugin"));
#endif
  if (!plugin)
    return false;

  IPlugin *pluginInstance = plugin();
  if (!pluginInstance)
    return false;

  LOG_INFO("Load Plugin: {}", pluginInstance->GetName());
  pluginInstance->pluginManager = this;
  plugins.emplace(pluginInstance->GetName(), pluginInstance);

  return true;
}

bool PluginManager::LoadPluginDir(const std::filesystem::path &dir)
{
  if (!std::filesystem::exists(dir))
    return false;

  for (const auto &entry : std::filesystem::directory_iterator(dir))
  {
    if (entry.is_regular_file() && entry.path().extension() == GetPluginExt())
    {
      LoadPlugin(entry.path().stem().string());
    }
  }
  return true;
}


IPlugin *PluginManager::GetPlugin(const std::string &name)
{
  auto it = plugins.find(name);
  if (it != plugins.end())
    return it->second;
  else
    return nullptr;
}

std::filesystem::path PluginManager::GetPluginDir()
{
#ifdef _WIN32
  // Windows 获取 exe 路径
  char exePath[MAX_PATH];
  GetModuleFileNameA(nullptr, exePath, MAX_PATH);
#else
  // Linux/macOS 获取 exe 路径
  char exePath[PATH_MAX];
  ssize_t count = readlink("/proc/self/exe", exePath, PATH_MAX); // macOS 使用 _NSGetExecutablePath
  if (count == -1)
    return false;
  exePath[count] = '\0';
#endif

  return std::filesystem::path(exePath).parent_path() / PluginManager::s_defaultPluginDir;
}

std::string PluginManager::GetPluginExt()
{
  // 跨平台扩展名处理
#ifdef _WIN32
  static constexpr auto plugin_ext = ".dll";
#elif __APPLE__
  static constexpr auto plugin_ext = ".dylib";
#else
  static constexpr auto plugin_ext = ".so";
#endif
  return plugin_ext;
}
